using System;
using System.Collections.Generic;
using System.Text;

using System.Data.SqlClient;

namespace Antix.Data.Catalogue.Objects
{
    #region Base

    public abstract class DatabaseObjectBase
    {
        #region Constructor/Defaults

        /// <summary>
        /// Create a scriptable class
        /// </summary>
        protected DatabaseObjectBase()
        {
        }

        #endregion

        #region Properties

        #endregion

        #region Methods

        #region Script

        /// <summary>
        /// Script this database object
        /// </summary>
        /// <param name="sql">StringBuilder</param>
        /// <param name="subObjects">Script sub-objects</param>
        /// <returns>Script created</returns>
        public abstract bool Script(StringBuilder sql, bool subObjects);

        #endregion

        #endregion
    }

    #endregion

    #region Object class

    public abstract class DatabaseObject : DatabaseObjectBase
    {
        #region Constructor/Defaults

        /// <summary>
        /// Create an existing database object class
        /// </summary>
        protected DatabaseObject(string nameOriginal)
        {
            _IsNew = false;
            _Name = new DatabaseObjectName(nameOriginal);
        }

        #endregion

        #region Properties

        #region Status

        #region IsNew

        private bool _IsNew;

        /// <summary>
        /// Object is new
        /// </summary>
        public bool IsNew
        {
            get { return _IsNew; }
            set { _IsNew = value; }
        }

        #endregion

        #region IsEdited

        private bool _IsEdited;

        /// <summary>
        /// Object has changed
        /// </summary>
        public bool IsEdited
        {
            get { return _IsEdited; }
            protected set { _IsEdited = value; }
        }

        #endregion

        #region IsDeleted

        private bool _IsDeleted;

        /// <summary>
        /// Object is deleted
        /// </summary>
        public bool IsDeleted
        {
            get { return _IsDeleted; }
            set { _IsDeleted = value; }
        }

        #endregion

        #endregion

        #region Key

        /// <summary>
        /// Get a key for this object
        /// </summary>
        public abstract string Key
        {
            get;
        }

        #endregion

        #region Name

        private DatabaseObjectName _Name;

        /// <summary>
        /// Name of the object
        /// </summary>
        public string Name
        {
            get { return _Name.Value; }
            set { _Name.Value = value; }
        }

        /// <summary>
        /// Original name of the object
        /// </summary>
        public string NameOriginal
        {
            get { return _Name.ValueOriginal; }
        }

        /// <summary>
        /// Name has been changed
        /// </summary>
        public bool NameIsEdited
        {
            get { return _Name.IsEdited; }
        }

        #endregion

        #endregion

        #region Methods

        #region Save

        /// <summary>
        /// Save changes to the database object
        /// </summary>
        /// <param name="conn">Connection</param>
        internal virtual void Save(SqlConnection conn)
        {
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;

            // get the sql for this object, to save sub objects this method should be overriden
            StringBuilder sql = new StringBuilder();
            this.Script(sql, false);

            if (sql.Length > 0)
            {
                // Execute at the GO statement
                foreach (string command in sql.ToString().Split(
                    new string[] { "\nGO\n" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    cmd.CommandText = command;
                    cmd.ExecuteNonQuery();
                }
            }

            // Reset the status
            _Name = new DatabaseObjectName(this.Name);
            this.IsDeleted = false;
            this.IsNew = false;
            this.IsEdited = false;
        }

        #endregion

        #endregion
    }

    #endregion

    #region Collection Class

    public abstract class DatabaseObjectCollection : DatabaseObjectBase, IEnumerable<DatabaseObject>
    {
        #region Constructor/Defaults

        /// <summary>
        /// Create a collection
        /// </summary>
        protected DatabaseObjectCollection() { }

        #endregion

        #region Properties

        protected DatabaseObjectBase this[string key]
        {
            get { return Items[key.ToLower()]; }
        }

        #region Count

        /// <summary>
        /// Number of items in the collection
        /// </summary>
        public int Count
        {
            get { return _Items == null ? 0 : _Items.Count; }
        }

        #endregion

        #region Items

        private Dictionary<string, DatabaseObject> _Items;

        private Dictionary<string, DatabaseObject> Items
        {
            get
            {
                if (_Items == null)
                {
                    _Items = new Dictionary<string, DatabaseObject>();
                }
                return _Items;
            }
        }
        
        #endregion

        #endregion

        #region Methods

        #region Clear

        /// <summary>
        /// Clear the collection
        /// </summary>
        public void Clear()
        {
            if (_Items != null) { _Items.Clear(); }
        }

        #endregion

        #region Contains

        /// <summary>
        /// Check key is in the items collection
        /// </summary>
        /// <param name="name">key</param>
        /// <returns>True if found</returns>
        public bool Contains(string key)
        {
            return Items.ContainsKey(key.ToLower());
        }

        #endregion

        #region Add

        /// <summary>
        /// Add item
        /// </summary>
        /// <param name="item">Item</param>
        protected void Add(DatabaseObject item)
        {
            this.Items.Add(item.Key.ToLower(), item);
        }

        #endregion

        #region Remove

        /// <summary>
        /// Remove an item
        /// </summary>
        public void Remove(string key)
        {
            _Items.Remove(key.ToLower());
        }

        #endregion    

        #region Script

        /// <summary>
        /// Script each item in the collection
        /// </summary>
        /// <param name="sql">Sql</param>
        /// <param name="subObjects">Script sub-objects</param>
        /// <returns>Script created</returns>
        public override bool Script(StringBuilder sql, bool subObjects)
        {
            bool scriptCreated = false;
            foreach (DatabaseObject item in this)
            {
                if (item.Script(sql, subObjects))
                {
                    scriptCreated = true;
                }
            }

            return scriptCreated;
        }

        #endregion

        #region Save

        /// <summary>
        /// Save each item in the collection
        /// </summary>
        /// <param name="conn">Connection</param>
        internal void Save(SqlConnection conn)
        {
            foreach (DatabaseObject item in this) { item.Save(conn); }
        }

        #endregion

        #endregion

        #region IEnumerable<DatabaseObject> Members

        /// <summary>
        /// Enumerate the collection
        /// </summary>
        /// <returns>IEnumerator</returns>
        public IEnumerator<DatabaseObject> GetEnumerator()
        {
            return Items.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Enumerate the collection
        /// </summary>
        /// <returns>IEnumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Items.Values.GetEnumerator();
        }

        #endregion
    }

    #endregion

    #region Object Name Class

    public class DatabaseObjectName
    {
        #region Consturctor

        public DatabaseObjectName(string value)
        {
            _Value = value;
            _ValueOriginal = value;
        }

        #endregion

        #region Properties

        #region Value

        private string _Value;
        private string _ValueOriginal;

        /// <summary>
        /// Value of the database object name
        /// </summary>
        public string Value
        {
            get { return _Value; }
            set { _Value = value; }
        }

        /// <summary>
        /// Original value of the database object name
        /// </summary>
        internal string ValueOriginal
        {
            get { return _ValueOriginal; }
        }

        #endregion

        #region IsEdited

        public bool IsEdited
        {
            get { return _ValueOriginal != _Value; }
        }

        #endregion

        #endregion
    }

    #endregion
}